/*
 * VectorRegistros.cpp
 *
 */
#include "VectorRegistros.h"


VectorRegistros::VectorRegistros(size_t tamBytes){
	this->_espacioLibre = tamBytes;
	this->_cantBytesOcupados =0;
}


VectorRegistros::~VectorRegistros(){
	this->_espacioLibre = 0;
	this->_cantBytesOcupados =0;

	// Libera el contenido de la lista
	std::list<RegistroAbstracto*>::iterator it;
	for (it=this->_listRegs.begin(); it != this->_listRegs.end(); it++){
		delete (*it);
	}

	// Limpia toda la lista
	this->_listRegs.clear();
}


resulOperacion VectorRegistros::addRegistro(RegistroAbstracto* reg){

	// No se permiten registros repetidos
	if ( this->existeRegistro(reg->getId()) ) return ERROR;

	// Itera a travez de todos los registros
	std::list<RegistroAbstracto*>::iterator it;
	for (it = this->_listRegs.begin(); it != this->_listRegs.end(); it++){

		// El registro apuntado por el iterator
		RegistroAbstracto* regTemp = *it;

		// Busca un registro con "id" mayor
		if ( regTemp->getId() > reg->getId() ) break;
	}

	// Si encontró la posicion donde insertar el registro
	if (it != this->_listRegs.end()){
		// Inserta el registro antes del apuntado
		this->_listRegs.insert(it, reg);
	} else {
		// No se encontro un registro con valor mayor, lo agregar al final
		this->_listRegs.push_back(reg);
	}

	// Actualiza los parametros del contenedor
	this->_espacioLibre -= reg->getSize();
	this->_cantBytesOcupados += reg->getSize();

	// Devuelve el resultado de la operacion
	if ( this->_espacioLibre <=0 ) return OVERFLOW;
	else return OK;
}


// Chequea si existe underflow en la lista
bool VectorRegistros::hayUnderFlow(){
	// Si el espacio libre es mayor que el espacio ocupado hay underflow
	return (this->_espacioLibre > this->_cantBytesOcupados);
}


resulOperacion VectorRegistros::removeRegistro(std::string id){

	// Tiene que encontrar el registro a borrar
	std::list<RegistroAbstracto*>::iterator it;
	for (it = this->_listRegs.begin(); it != this->_listRegs.end(); it++){
		RegistroAbstracto* reg = *it;

		// Si tiene el mismo Id hay que borrarlo
		if ( reg->getId() == id ){
			this->_listRegs.erase(it);

			this->_espacioLibre += reg->getSize();
			this->_cantBytesOcupados -= reg->getSize();

			// Cheque el resultado de la operacion
			if ( hayUnderFlow() ) return UNDERFLOW;
			else return OK;
		}
	}

	// No se encontro el elemento a borrar
	return ERROR;
}


RegistroAbstracto* VectorRegistros::getRegistro(std::string id){

	// Busca el registro
	std::list<RegistroAbstracto*>::iterator it;
	for (it = this->_listRegs.begin(); it != this->_listRegs.end(); it++){
		RegistroAbstracto* reg = *it;
		if ( reg->getId() == id ) return *it;
	}

	return NULL;
}


size_t VectorRegistros::getSize(){
	// El tamaño para la metadata del contenedor
	size_t tam = sizeof(int) + sizeof(int);
	
	// El tamaño de los registros
	tam += this->_cantBytesOcupados;

	return tam;
}


bool VectorRegistros::existeRegistro(std::string id){
	// Recorre todos los elementos buscando uno que tenga el ud señalado
	std::list<RegistroAbstracto*>::iterator it;
	for (it = this->_listRegs.begin(); it != this->_listRegs.end(); it++){

		RegistroAbstracto* reg = *it;
		if (reg->getId() == id) return true;
	}

	// No se encontro
	return false;
}


size_t VectorRegistros::getCantRegistros(){
	return this->_listRegs.size();
}


RegistroAbstracto* VectorRegistros::operator[](int i){
	std::list<RegistroAbstracto*>::iterator it = this->_listRegs.begin();
	for (int j=0; j<i; j++) it++;

	return *it;
}


// Pasan los elementos mayores
bool VectorRegistros::split(VectorRegistros* newContenedor){

	// Calcula la posicion desde donde se deben copiar los elementos
	// al nuevo contenedor
	int cantElems = this->_listRegs.size()/2;
	std::list<RegistroAbstracto*>::iterator it = this->_listRegs.begin();
	for (int i=0; i< cantElems; i++) it++;

	// Mueve los elementos al nuevo contenedor
	for (std::list<RegistroAbstracto*>::iterator it2 = it; it2 != this->_listRegs.end(); it2++){
		newContenedor->addRegistro(*it2);
	}

	// Elimina los registros de la lista local
	this->_listRegs.erase(it, this->_listRegs.end());

	return true;
}


bool VectorRegistros::merge(VectorRegistros* oldContenedor){

	// Copia los registros del contenedor a mi mismo
	for (size_t i=0; i< oldContenedor->getCantRegistros(); i++){
		RegistroAbstracto* reg = (*oldContenedor)[i];
		this->addRegistro(reg);
	}
	
	return true;
}


void VectorRegistros::pack(char* &data){
	// Primero se guarda el tamaño total y la cant de registros
	SerialInator serial;
	serial.serializar(data, static_cast<int>(this->_cantBytesOcupados) );
	serial.serializar(data, static_cast<int>(this->_listRegs.size()) );

	// Guarda una referencia donde ir agregando los datos
	char* ptr = serial.getPtroActual(data);

	// Se guardan los registros de a uno
	std::list<RegistroAbstracto*>::iterator it;
	for (it = this->_listRegs.begin(); it != this->_listRegs.end(); it++){
		RegistroAbstracto* reg = *it;

		// Almacenan los registros
		reg->pack(ptr);
		ptr += reg->getSize();
	}
}


void VectorRegistros::unpack(char* data, datosArchivados tipo, bool esHoja){
	// Primero viene el tamaño y despues la cantidad de regs
	int tam, cantRegs;
	SerialInator serial;
	serial.desSerializar(data, tam);
	serial.desSerializar(data, cantRegs);

	// Un puntero a los datos que pertenecen al primer registro
	char* ptrData = serial.getPtroActual(data);

	// Luego vienen los registros
	for (int i=0; i< cantRegs; i++){
		// Crea el registro correspondiente
		RegistroAbstracto* reg = RegistroAbstracto::unPack(ptrData, tipo, esHoja);
		ptrData += reg->getSize();

		// Agrega el registro al final
		this->_listRegs.push_back(reg);
		this->_espacioLibre -= reg->getSize();
		this->_cantBytesOcupados += reg->getSize();
	}
}


